﻿
Imports System.Net
Imports System.Text
Imports std
Imports System.Threading

Public Module fakehttprequest

    Private Class arguments
        Public Const urlpattern As String = "u"
        Public Const threadCount As String = "t"
        Public Const testSize As String = "s"

        Public Class defaultValue
            Public Const threadCount As Int64 = 32
            Public Const testSize As Int64 = 1000000
        End Class
    End Class

    Private finished As Int64 = 0
    Private startticks As Int64 = Now.Ticks()

    Private Sub writeQPS()
        If Now.Ticks() - startticks >= secondsToTicks(1) Then
            Static last As Int64
            Dim nowfinished As Int64
            Threading.Interlocked.Exchange(nowfinished, finished)
            Console.Write("Last QPS " + strfill(Convert.ToString(nowfinished - last), character.blank, 8) +
                          "     QPS " + strfill(Convert.ToString(nowfinished \ ticksToSeconds(Now.Ticks() - startticks)),
                                                character.blank, 8) +
                          character.return)
            last = nowfinished
        End If
    End Sub

    'url pattern, \ means one character, * means several characters
    Private Function generateUrl(ByVal pattern As String) As String
        Dim rtn As StringBuilder = Nothing
        rtn = New StringBuilder()
        Dim i As Int64
        For i = 0 To strlen(pattern) - 1
            If pattern(i) = character.rightSlash Then
                rtn.Append(rndenchar())
            ElseIf pattern(i) = character.multiplicationSign Then
                rtn.Append(rndenchars(rnd(0, 10, True)))
            Else
                rtn.Append(pattern(i))
            End If
        Next

        Return Convert.ToString(rtn)
    End Function

    Private pool As webClientPool = Nothing
    Private ce As countEvent = Nothing

    Private Class webClientPool
        Inherits pool(Of WebClient)

        Public Sub New(Optional ByVal buffsize As Int64 = defaultBuffCount)
            MyBase.New(buffsize)
        End Sub

        Protected Overrides Function create() As WebClient
            Dim rtn As WebClient = Nothing
            rtn = New WebClient()
            AddHandler rtn.DownloadDataCompleted,
                       Sub(sender As Object, e As DownloadDataCompletedEventArgs)
                           If Not e.Cancelled AndAlso e.Error Is Nothing Then
                               Interlocked.Increment(finished)
                           End If
                           pool.release(Convert.ToInt64(e.UserState))
                           ce.set()
                       End Sub
            Return rtn
        End Function
    End Class

    Private Sub workthread(ByVal obj As Object)
        Dim wc As WebClient = Nothing
        Dim index As Int64 = 0
        While Not pool.getfree(index, wc)
            ce.wait()
        End While
        Dim url As String = Nothing
        url = convertor.cast(Of String)(obj)
        wc.DownloadDataAsync(New Uri(url), index)
    End Sub

    Private Sub start(ByVal urlpattern As String, ByVal threadCount As Int64, ByVal testSize As Int64)
        loosenSPMLimit(threadCount << 1)
        timer3.addcall(AddressOf writeQPS, secondsToMilliseconds(1))
        Dim tp As threadpool = Nothing
        tp = resolver.global.resolve(Of ithreadpool)()
        pool = New webClientPool(threadCount)
        ce = New countEvent()
        For i As Int64 = 0 To threadCount - 1
            ce.set()
        Next

        If testSize < 0 Then
            testSize = maxInt64 \ threadCount
        End If

        For i As Int64 = 0 To threadCount * testSize - 1
            If tp.queueLength() < (tp.threadCount << 1) Then
                tp.insertJob(AddressOf workthread, generateUrl(urlpattern))
            Else
                i -= 1
                sleep(1)
            End If
        Next
    End Sub

    Public Sub main(ByVal iargs() As String)
        Dim args As map(Of String, vector(Of String)) = Nothing
        argmgr.parse(iargs, args, True)

        Dim urlpattern As String = Nothing
        If Not argmgr.getfirstvalue(args, arguments.urlpattern, urlpattern) Then
            raiseError("do not have urlpattern -" + arguments.urlpattern + " in arguments, cannot start.", _
                       errorHandle.errorType.user)
        Else
            Dim threadCountStr As String = Nothing
            Dim testSizeStr As String = Nothing
            Dim threadCount As Int64
            Dim testSize As Int64
            If Not argmgr.getfirstvalue(args, arguments.threadCount, threadCountStr, True) OrElse _
               Not convertor.toint64(threadCountStr, threadCount) Then
                threadCount = arguments.defaultValue.threadCount
                raiseError("do not have threadCount -" + arguments.threadCount + _
                           " in arguments, use default value as " + _
                           Convert.ToString(arguments.defaultValue.threadCount))
            End If
            If Not argmgr.getfirstvalue(args, arguments.testSize, testSizeStr, True) OrElse _
               Not convertor.toint64(testSizeStr, testSize) Then
                testSize = arguments.defaultValue.testSize
                raiseError("do not have testSize -" + arguments.testSize + _
                           " in arguments, use default value as " + Convert.ToString(arguments.defaultValue.testSize))
            End If

            threadpool.register()
            start(urlpattern, threadCount, testSize)
        End If
    End Sub

End Module
